package com.rh.number.service.impl;

import com.rh.number.dto.FilterParam;
import com.rh.number.dto.FilterOption;
import com.rh.number.entity.numberEntity;
import com.rh.number.mapper.NumberMapper;
import jakarta.persistence.Column;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
public class NumberFilterService {
    private final NumberMapper numberMapper;
    private static final Logger log = LoggerFactory.getLogger(NumberFilterService.class);

    public NumberFilterService(NumberMapper numberMapper) {
        this.numberMapper = numberMapper;
    }
    private static final Set<String> FILTER_FIELDS = Set.of(
            "client", "state", "exclusiveStatus", "businessScenario",
            "province", "city", "type", "upstream", "monthlyRent",
            "concurrent", "controlFrequency", "project", "salesMonthlyRent",
            "remark", "effectiveDate", "removalTime"
    );

    public Map<String, List<FilterOption>> dynamicFilter(FilterParam param) {
        // 使用安全获取方式
        List<String> targetFields = Optional.ofNullable(param.getTargetFields())
                .filter(list -> !list.isEmpty())
                .orElse(new ArrayList<>(FILTER_FIELDS));

        // 空集合校验
        if (targetFields.isEmpty()) {
            throw new IllegalArgumentException("至少需要指定一个统计字段");
        }

        // 转换过滤条件
        Map<String, String> dbFilters = convertFilters(param.getCurrentFilters());

        return targetFields.stream()
                .collect(Collectors.toMap(
                        field -> field,
                        field -> getFilterResults(field, dbFilters)
                ));
    }

    private List<FilterOption> getFilterResults(String field, Map<String, String> filters) {
        String dbColumn = getDatabaseColumnName(field);
        return numberMapper.getFilterOptions(dbColumn, filters);
    }

    private Map<String, String> convertFilters(Map<String, String> filters) {
        Map<String, String> dbFilters = Optional.ofNullable(filters)
                .orElse(Collections.emptyMap())
                .entrySet()
                .stream()
                .filter(e -> {
                    log.debug("原始过滤条件: {}={}", e.getKey(), e.getValue()); // [!code focus]
                    return e.getValue() != null && !e.getValue().isEmpty();
                })
                .collect(Collectors.toMap(
                        e -> {
                            String dbColumn = getDatabaseColumnName(e.getKey());
                            log.debug("字段转换: 前端[{}]→数据库[{}]", e.getKey(), dbColumn); // [!code focus]
                            return dbColumn;
                        },
                        Map.Entry::getValue
                ));
        log.debug("最终过滤条件: {}", dbFilters); // [!code focus]
        return dbFilters;
    }

    private void validateFields(List<String> fields) {
        fields.forEach(field -> {
            if (!FILTER_FIELDS.contains(field)) {
                throw new IllegalArgumentException("非法字段: " + field);
            }
        });
    }

    /**
     * 通过JPA注解获取数据库列名
     */
    private String getDatabaseColumnName(String field) {
        try {
            Field classField = numberEntity.class.getDeclaredField(field);
            Column column = classField.getAnnotation(Column.class);
            return (column != null && !column.name().isEmpty()) ?
                    column.name() :
                    camelToSnake(field);
        } catch (NoSuchFieldException e) {
            throw new IllegalArgumentException("无效字段: " + field);
        }
    }

    /**
     * 驼峰转下划线命名
     */
    private String camelToSnake(String camel) {
        StringBuilder result = new StringBuilder();
        for (char c : camel.toCharArray()) {
            if (Character.isUpperCase(c)) {
                result.append('_').append(Character.toLowerCase(c));
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }
}